package datafeed;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class LiveDataFeed implements IDataFeed {
	
	private static LiveDataFeed m_instance;
	public static LiveDataFeed getInstance() {
		if(m_instance == null)
			m_instance = new LiveDataFeed();
		return m_instance;
	}
	
	private List<String> items = new ArrayList<String>();
	private boolean initialized;
	private URL codeBase;
	
	private List<Category> m_categories = new ArrayList<Category>();
	private List<DataItem> m_dataItems = new ArrayList<DataItem>();
	
	private LiveDataFeed() {
		initialized = false;
		items.add("Temperature");
		items.add("Kilowatt Hours Used");
		items.add("Solar Panel Output");
		items.add("Wind Speed");
		items.add("Traffic");
		items.add("HVAC Load");
		items.add("WiFi Users");
	}
	
	public void setCodeBase(URL cb) {
		if(cb.toString().contains("C:/")) {
			try {
				codeBase = new URL("http://localhost:8080/InteractiveDisplayApplet");
			}
			catch(MalformedURLException ex) {
				ex.printStackTrace();
			}
		}
		else {
			codeBase = cb;
		}
	}
	
	public void initialize() {
		loadCategories();
		loadDataItems();
		initialized = true;
	}
	
	private void loadCategories() {
		try {
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			URL url = new URL(codeBase.toString().replace("/bin", "") + "/BacNetDataFeed.jsp?type=Categories");
			URLConnection source = url.openConnection();
			Document doc = db.parse(source.getInputStream());
			NodeList items = doc.getElementsByTagName("category");
			for(int i = 0; i < items.getLength(); i++) {
				NodeList children = items.item(i).getChildNodes();
				for(int j = 0; j < children.getLength(); j++) {
					if(children.item(j).getNodeName().equals("name")) {
						Category category = new Category(children.item(j).getFirstChild().getNodeValue());
						m_categories.add(category);
					}
				}
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void loadDataItems() {
		String name = "";
		Category category = null;
		String units = "";
		try {
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			URL url = new URL(codeBase.toString().replace("/bin", "") + "/BacNetDataFeed.jsp?type=DataItems");
			URLConnection source = url.openConnection();
			Document doc = db.parse(source.getInputStream());
			NodeList items = doc.getElementsByTagName("dataitem");
			for(int i = 0; i < items.getLength(); i++) {
				NodeList children = items.item(i).getChildNodes();
				for(int j = 0; j < children.getLength(); j++) {
					if(children.item(j).getNodeName().equals("name")) {
						name = children.item(j).getFirstChild().getNodeValue();
					}
					else if(children.item(j).getNodeName().equals("category")) {
						category = getCategoryByName(children.item(j).getFirstChild().getNodeValue());
					}
					else if(children.item(j).getNodeName().equals("units")) {
						units = children.item(j).getFirstChild().getNodeValue();
					}
				}
				DataItem dataItem = new DataItem(name, units, category);
				
				m_dataItems.add(dataItem);
				category.addDataItem(dataItem);
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private Category getCategoryByName(String name) {
		for(Category c : m_categories) {
			if(name.equals(c.getName())) return c;
		}
		return null;
	}
	
	@Override
	public void getDataItemsByCategory(List<String> dataItems, String category) {
		Category target = getCategoryByName(category);
		dataItems.clear();
		if(target != null) {
			for(DataItem d : target.getDataItems())
				dataItems.add(d.getName());
		}
	}
	
	@Override
	public void getTimeScales(List<String> timeScales) {
		timeScales.clear();
		timeScales.add("hour");
		timeScales.add("day");
		timeScales.add("week");
	}

	@Override
	public void getDataItems(List<String> dataItems) {
		dataItems.clear();
		for(DataItem d : m_dataItems) {
			dataItems.add(d.getName());
		}
	}

	@Override
	public void getCategories(List<String> categories) {
		categories.clear();
		for(Category c : m_categories) {
			categories.add(c.getName());
		}
	}

	@Override
	public String getChartData(Map<String, List<DataPoint>> data, Calendar start,
			Calendar end, int numPoints, List<String> dataItems) throws Exception {
		
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
		String timescale = "";
		
		try {
			
			//get data from server
			String postParams = "type=Data" + 
			"&startTime=" + df.format(start.getTime()) + 
			"&endTime=" + df.format(end.getTime()) + 
			"&numPoints=" + numPoints;
			int num = 0;
			for(String s : dataItems) postParams += "&dataItem" + (num++) + "=" + s;
			
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		    URL url = new URL(codeBase.toString().replace("/bin", "") + "/BacNetDataFeed.jsp");
		    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		    connection.setRequestMethod("POST");
		    connection.setRequestProperty("Content-Type",
		            "application/x-www-form-urlencoded");
		    connection.setRequestProperty("Content-Length", ""
		            + Integer.toString(postParams.getBytes().length));
		    connection.setRequestProperty("Content-Language", "en-US");
		
		    connection.setUseCaches(false);
		    connection.setDoInput(true);
		    connection.setDoOutput(true);
		
		    // Send request
		    DataOutputStream wr = new DataOutputStream(connection
		            .getOutputStream());
		    wr.writeBytes(postParams);
		    wr.flush();
		    wr.close();
		
		    // Get Response
			Document doc = db.parse(connection.getInputStream());

			//find the timescale
			NodeList items = doc.getElementsByTagName("timescale");
			timescale = items.item(0).getFirstChild().getNodeValue();
			
			//parse the xml data for each dataItem
			for(String dataItem : dataItems) {
				List<DataPoint> dataPoints = new ArrayList<DataPoint>();
				//find the correct dataItem
				items = doc.getElementsByTagName("dataitem");
				Node dataItemNode = null;
				Node dataListNode = null;
				for(int i = 0; i < items.getLength(); i++) {
					NodeList children = items.item(i).getChildNodes();
					for(int j = 0; j < children.getLength(); j++) {
						if(children.item(j).getNodeName().equals("name")) {
							String name = children.item(j).getFirstChild().getNodeValue();
							if(name.equals(dataItem)) dataItemNode = items.item(i);
						}
					}
				}
				if(dataItemNode == null) { 
					System.out.println("data item " + dataItem + " not found in http response from server");
					continue;
				}
				//get the dataList node
				items = dataItemNode.getChildNodes();
				for(int i = 0; i < items.getLength(); i++) {
					if(items.item(i).getNodeName().equals("datalist")) {
						dataListNode = items.item(i);
					}
				}
				//parse the dataList node for data
				items = dataListNode.getChildNodes();
				for(int i = 0; i < items.getLength(); i++) {
					if(items.item(i).getNodeName().equals("data")) {
						DataPoint dp = new DataPoint();
						NodeList dataNodeList = items.item(i).getChildNodes();
						for(int k = 0; k < dataNodeList.getLength(); k++) {
							if(dataNodeList.item(k).getNodeName().equals("time")) {
								dp.date = df.parse(dataNodeList.item(k).getFirstChild().getNodeValue());
								//System.out.println("timestamp: " + df.format(dp.date));
							}
							else if(dataNodeList.item(k).getNodeName().equals("value")) {
								dp.value = Double.parseDouble(dataNodeList.item(k).getFirstChild().getNodeValue());
								//System.out.println("value: " + dp.value);
							}
						}
						dataPoints.add(dp);
					}
//					NodeList children = items.item(i).getChildNodes();
//					for(int j = 0; j < children.getLength(); j++) {
//						if(children.item(j).getNodeName().equals("data")) {
//						}
//					}
				}
				data.put(dataItem, dataPoints);
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return timescale;
	}
}
